package net.lab0.tools.evo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import net.lab0.tools.Pair;
import net.lab0.tools.StaticRandom;
import net.lab0.tools.evo.iface.Individual;
import net.lab0.tools.evo.iface.Selection;

public class Tournament<I extends Individual<I>>
implements Selection<I>
{
    private int size;
    
    /**
     * Creates a tournament of the wanted size.
     * 
     * @param size
     *            The size of the candidates pool for this tournament. Must be at least 2.
     * @throws IllegalArgumentException
     *             if the size is incorrect.
     */
    public Tournament(int size)
    throws IllegalArgumentException
    {
        super();
        if (size < 2)
        {
            throw new IllegalArgumentException("The size of a turnament must be at least 2.");
        }
        this.size = size;
    }
    
    /**
     * @throws IllegalArgumentException
     *             If the population is too small.
     */
    @Override
    public List<Pair<I, I>> selectIndividuals(List<I> individuals, int pairsCount)
    throws IllegalArgumentException
    {
        // check that it is possible to do the tournament
        int popuplationSize = individuals.size();
        if (popuplationSize < pairsCount * 2)
        {
            throw new IllegalArgumentException(
            "The number of individuals in the population must be at least twice the number of wanted pairs.");
        }
        
        // the returned list.
        List<Pair<I, I>> ret = new ArrayList<>(pairsCount);
        
        // the list used for the selection of the candidates for each tournament
        List<I> candidates = new ArrayList<>(size);
        
        // the population from which the individuals will be selected
        List<I> population = new LinkedList<>(individuals);
        
        // do the wanted count of tournaments
        for (int i = 0; i < pairsCount; i++)
        {
            candidates.clear();
            
            for (int j = 0; j < size; j++)
            {
                candidates.add(population.remove(StaticRandom.random.nextInt(popuplationSize)));
                popuplationSize--;
            }
            
            // keep the 2 best candidates
            Collections.sort(candidates, new FitnessComparator());
            
            int candidatesCount = candidates.size();
            
            ret.add(new Pair<I, I>(candidates.get(candidatesCount - 2), candidates.get(candidatesCount - 1)));
        }
        
        return ret;
    }
}
